package com.komon.object.response;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import com.komon.constant.Configuration;

public class HttpResponse implements HttpServletResponse{
	//allow to modify status
	private StatusLine statusLine;
	private Map<String,String> headers = new HashMap<String, String>();
	//output
	private PrintWriter bodyWriter;
	private ResponseOutputStream servletOutputStream;
	private final OutputStream socketOutputStream;
	//char encoding
	private volatile String charSet = Configuration.DEFAULT_RESP_BODY_ENCODING;
	
	public HttpResponse(int statusCode, OutputStream socketOutputStream) throws UnsupportedEncodingException {
		this(new StatusLine(statusCode), new HashMap<String,String>(), socketOutputStream);
	}
	
	public HttpResponse(StatusLine statusLine, Map<String, String> headers, OutputStream socketOutputStream) throws UnsupportedEncodingException {
		this.statusLine = statusLine;
		this.headers = headers;
		this.socketOutputStream = socketOutputStream;
		//
	}
	
	//TODO
	private ResponseOutputStream initConcretOutputStreamByHeaders(){
		if (this.containsHeader("Transfer-Encoding")) {
			return new ChunkedResponseOutputStream(this,socketOutputStream);
		}else if (this.containsHeader("Content-Length")) {
			return new FixedLengthResponseOutputStream(this,socketOutputStream);
		}else {
			//TODO
			return null;
		}
	}
	
	public PrintWriter getWriter() throws IOException {
		if (bodyWriter == null) {
			bodyWriter =  new PrintWriter(new OutputStreamWriter(this.getOutputStream(), this.charSet));
		}
		return bodyWriter;
	}


	/**
	 * @return the statusLine
	 */
	public StatusLine getStatusLine() {
		return statusLine;
	}

	/**
	 * @return the headers
	 */
	public Map<String, String> getHeaders() {
		return headers;
	}


	public void addCookie(Cookie cookie) {
		// TODO Auto-generated method stub
		
	}


	public void addDateHeader(String name, long date) {
		// TODO Auto-generated method stub
	}

	public void addHeader(String name, String value) {
		String oldValue = this.getHeader(name);
		if (oldValue != null) {
			this.getHeaders().put(name, oldValue + "," + value);
		}else {
			this.getHeaders().put(name, value);
		}
	}


	public void addIntHeader(String name, int value) {
		addHeader(name, String.valueOf(value));
	}


	public boolean containsHeader(String name) {
		return this.getHeader(name) != null;
	}


	public String encodeRedirectURL(String url) {
		// TODO Auto-generated method stub
		return null;
	}


	public String encodeRedirectUrl(String url) {
		// TODO Auto-generated method stub
		return null;
	}


	public String encodeURL(String url) {
		// TODO Auto-generated method stub
		return null;
	}


	public String encodeUrl(String url) {
		// TODO Auto-generated method stub
		return null;
	}


	public void sendError(int sc, String msg) throws IOException {
		// TODO Auto-generated method stub
		
	}


	public void sendError(int sc) throws IOException {
		// TODO Auto-generated method stub
		
	}


	public void sendRedirect(String location) throws IOException {
		// TODO Auto-generated method stub
		
	}


	public void setDateHeader(String name, long date) {
		// TODO Auto-generated method stub
		
	}


	public void setHeader(String name, String value) {
		//case insensitive
		for (Map.Entry<String, String> entry : headers.entrySet()) {
			if (entry.getKey().equalsIgnoreCase(name)) {
				entry.setValue(value);
				return ;
			}
		}
		this.getHeaders().put(name, value);
	}
	
	public String getHeader(String name){
		//case insensitive
		for (Map.Entry<String, String> entry : headers.entrySet()) {
			if (entry.getKey().equalsIgnoreCase(name)) {
				return entry.getValue();
			}
		}
		return null;
	}
	
	
	
	public void setIntHeader(String name, int value) {
		this.setHeader(name, String.valueOf(value));
	}


	public void setStatus(int sc) {
		
		
	}


	public void setStatus(int sc, String sm) {
		// TODO Auto-generated method stub
		
	}


	public void flushBuffer() throws IOException {
		//to ensure flush, you should call the toppest layer
		this.getWriter().flush();
	}


	public int getBufferSize() {
		return this.servletOutputStream.getBufferSize();
	}


	public String getCharacterEncoding() {
		return this.charSet;
	}


	public String getContentType() {
		return this.getHeader("Content-Type");
	}

	public Locale getLocale() {
		//TODO
		return null;
	}


	public ServletOutputStream getOutputStream() throws IOException {
		if (servletOutputStream == null) {
			servletOutputStream = initConcretOutputStreamByHeaders();
		}
		return servletOutputStream;
	}


	public boolean isCommitted() {
		return this.servletOutputStream.isCommited();
	}


	public void reset() {
		if (isCommitted()) {
			throw new IllegalArgumentException();
		} else {
			//TODO
		}
	}

	public void resetBuffer() {
		if (isCommitted()) {
			throw new IllegalArgumentException();
		}else{
			servletOutputStream.clearBuffer();
		}
	}


	public void setBufferSize(int size) {
		//TODO: actually we support change the size even has already write to the  body
		if (this.servletOutputStream.isWritten()) {
			throw new IllegalArgumentException();
		} else {
			this.servletOutputStream.setBufferSize(size);
		}
	}


	public void setCharacterEncoding(String charset) {
		this.charSet = charset;
		this.setHeader("", charset);
		//TODO
	}

	public void setContentLength(int len) {
		this.setHeader("Content-Length ", String.valueOf(len));
	}


	public void setContentType(String type) {
		this.setHeader("Content-Type", type);
	}


	public void setLocale(Locale loc) {
		// TODO Auto-generated method stub
	}
	
	public void close() throws IOException{
		this.flushBuffer();
		this.getWriter().close();
	}
}
